package com.jxpanda.commons.digest;

import com.jxpanda.commons.toolkit.Base64Kit;
import com.jxpanda.commons.toolkit.HexKit;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * 抽象的摘要算法对象
 * java自带的摘要算法其实已经够用了，但是API不太好用
 * 这里只是做一个门面设计，提供更好用的API
 *
 * @author Panda
 */
@Slf4j
@Getter
@Setter
@AllArgsConstructor
public abstract class AbstractDigest {

    /**
     * 算法枚举
     */
    private DigestAlgorithm algorithm;



    /**
     * 计算摘要值
     */
    public byte[] digest(byte[] input) {
        MessageDigest instance = instance();
        return instance == null ? new byte[0] : instance.digest(input);
    }

    public byte[] digest(String input) {
        return digest(input, StandardCharsets.UTF_8);
    }

    public byte[] digest(String input, Charset charset) {
        return digest(input.getBytes(charset));
    }

    /**
     * 计算摘要后，以16进制字符串的形式输出(默认大写)
     *
     * @param input 输入值
     * @return 摘要计算后的16进制字符串
     */
    public String digestToHex(String input) {
        return digestToHex(input, StandardCharsets.UTF_8);
    }

    /**
     * 计算摘要后，以16进制字符串的形式输出(默认大写)
     *
     * @param input 输入值
     * @return 摘要计算后的16进制字符串
     */
    public String digestToHex(String input, Charset charset) {
        return digestToHex(input.getBytes(charset), false);
    }

    /**
     * 计算摘要后，以16进制字符串的形式输出(默认大写)
     *
     * @param input 输入值
     * @return 摘要计算后的16进制字符串
     */
    public String digestToHex(byte[] input) {
        return digestToHex(input, false);
    }

    /**
     * 计算摘要后，以16进制字符串的形式输出
     *
     * @param input     输入值
     * @param lowerCase true的话，输出小写字符的16进制字符串，false则是大写
     * @return 摘要计算后的16进制字符串
     */
    public String digestToHex(byte[] input, boolean lowerCase) {
        return HexKit.toHex(digest(input), lowerCase);
    }


    /**
     * 计算摘要后，以16进制字符串的形式输出(默认大写)
     *
     * @param input 输入值
     * @return 摘要计算后的16进制字符串
     */
    public String digestToBase64(String input) {
        return digestToBase64(input, StandardCharsets.UTF_8);
    }

    /**
     * 计算摘要后，以16进制字符串的形式输出(默认大写)
     *
     * @param input 输入值
     * @return 摘要计算后的16进制字符串
     */
    public String digestToBase64(String input, Charset charset) {
        return digestToBase64(input.getBytes(charset));
    }

    /**
     * 计算摘要后，以16进制字符串的形式输出
     *
     * @param input 输入值
     * @return 摘要计算后的16进制字符串
     */
    public String digestToBase64(byte[] input) {
        return Base64Kit.encodeToString(digest(input));
    }

    /**
     * 返回一个指定算法的对象
     * 这里不实用单例模式
     * 查看源码后发现，这里的加密/解密过程是不支持并发的
     * 所以最安全的方案是每次使用都用一个新的对象
     */
    private MessageDigest instance() {
        try {
            return MessageDigest.getInstance(this.algorithm.getValue());
        } catch (NoSuchAlgorithmException e) {
            log.error("[MESSAGE DIGEST ERROR] No Such Algorithm", e);
        }
        return null;
    }

}
